package internal

import (
	"encoding/json"
	"github.com/zeast/logs"
	"sync"
)

type ServiceSyncer struct {
	sync.Mutex
	curDoveMap map[string]string   //服务名字对应的 dovekey
	curEtcdMap map[string][]string //服务名称对应该服务下的实例信息
	doveCli    *DoveClient
}

func NewServiceSyncer() *ServiceSyncer {
	return &ServiceSyncer{
		curDoveMap: make(map[string]string),
		curEtcdMap: make(map[string][]string),
	}
}

func (s *ServiceSyncer) SetDoveCli(cli *DoveClient) {
	s.doveCli = cli
}

func (s *ServiceSyncer) SetSer(name string, info []string) error {
	s.Lock()
	defer s.Unlock()

	s.curEtcdMap[name] = info
	return s.syncSer(name)
}

func (s *ServiceSyncer) DelSer(name string, ser string) {
	s.Lock()
	defer s.Unlock()

	if _, ok := s.curEtcdMap[name]; !ok {
		logs.Errorf("unknown service %s", name)
		return
	}

	for i, se := range s.curEtcdMap[name] {
		if se == ser {
			s.curEtcdMap[name] = append(s.curEtcdMap[name][:i], s.curEtcdMap[name][i+1:]...)
			if err := s.syncSer(name); err != nil {
				logs.Error(err)
			}
		}
	}

	return
}

func (s *ServiceSyncer) PutSer(name string, ser string) {
	s.Lock()
	defer s.Unlock()

	s.curEtcdMap[name] = append(s.curEtcdMap[name], ser)
	if err := s.syncSer(name); err != nil {
		logs.Error(err)
	}
}

// syncSer need locked
func (s *ServiceSyncer) syncSer(serviceName string) error {
	cnfName, ok := s.curDoveMap[serviceName]
	if !ok {
		return nil
	}

	var b []byte
	if _, ok := s.curEtcdMap[serviceName]; ok {
		var err error
		b, err = json.Marshal(s.curEtcdMap[serviceName])
		if err != nil {
			return err
		}
	}

	if err := s.doveCli.UpdateDoveKey(cnfName, b); err != nil {
		logs.Error(err, string(b), s.curEtcdMap[serviceName])
		return err
	}

	if err := s.doveCli.PublishDoveKey(cnfName); err != nil {
		return err
	}

	return nil
}

func (s *ServiceSyncer) SetDoveMap(doveMap map[string]string) {
	s.Lock()
	defer s.Unlock()

	var old = make(map[string]string)
	for k, v := range s.curDoveMap {
		old[k] = v
	}

	s.curDoveMap = doveMap

	for k, v := range doveMap {
		if ov, ok := old[k]; !ok || v != ov {
			if err := s.syncSer(k); err != nil {
				logs.Errorf("sync service failed, service name:%s, new value:%s, old value:%s", k, v, ov)
			} else {
				logs.Debugf("sync service success, service name:%s, new value:%s, old value:%s", k, v, ov)

			}
		}
	}
}
